home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / lib / pymodules / python2.6 / BeautifulSoup.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-11-02  |  69KB  |  2,011 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''Beautiful Soup
  5. Elixir and Tonic
  6. "The Screen-Scraper\'s Friend"
  7. http://www.crummy.com/software/BeautifulSoup/
  8.  
  9. Beautiful Soup parses a (possibly invalid) XML or HTML document into a
  10. tree representation. It provides methods and Pythonic idioms that make
  11. it easy to navigate, search, and modify the tree.
  12.  
  13. A well-formed XML/HTML document yields a well-formed data
  14. structure. An ill-formed XML/HTML document yields a correspondingly
  15. ill-formed data structure. If your document is only locally
  16. well-formed, you can use this library to find and process the
  17. well-formed part of it.
  18.  
  19. Beautiful Soup works with Python 2.2 and up. It has no external
  20. dependencies, but you\'ll have more success at converting data to UTF-8
  21. if you also install these three packages:
  22.  
  23. * chardet, for auto-detecting character encodings
  24.   http://chardet.feedparser.org/
  25. * cjkcodecs and iconv_codec, which add more encodings to the ones supported
  26.   by stock Python.
  27.   http://cjkpython.i18n.org/
  28.  
  29. Beautiful Soup defines classes for two main parsing strategies:
  30.  
  31.  * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific
  32.    language that kind of looks like XML.
  33.  
  34.  * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid
  35.    or invalid. This class has web browser-like heuristics for
  36.    obtaining a sensible parse tree in the face of common HTML errors.
  37.  
  38. Beautiful Soup also defines a class (UnicodeDammit) for autodetecting
  39. the encoding of an HTML or XML document, and converting it to
  40. Unicode. Much of this code is taken from Mark Pilgrim\'s Universal Feed Parser.
  41.  
  42. For more than you ever wanted to know about Beautiful Soup, see the
  43. documentation:
  44. http://www.crummy.com/software/BeautifulSoup/documentation.html
  45.  
  46. Here, have some legalese:
  47.  
  48. Copyright (c) 2004-2009, Leonard Richardson
  49.  
  50. All rights reserved.
  51.  
  52. Redistribution and use in source and binary forms, with or without
  53. modification, are permitted provided that the following conditions are
  54. met:
  55.  
  56.   * Redistributions of source code must retain the above copyright
  57.     notice, this list of conditions and the following disclaimer.
  58.  
  59.   * Redistributions in binary form must reproduce the above
  60.     copyright notice, this list of conditions and the following
  61.     disclaimer in the documentation and/or other materials provided
  62.     with the distribution.
  63.  
  64.   * Neither the name of the the Beautiful Soup Consortium and All
  65.     Night Kosher Bakery nor the names of its contributors may be
  66.     used to endorse or promote products derived from this software
  67.     without specific prior written permission.
  68.  
  69. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  70. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  71. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  72. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  73. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  74. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  75. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  76. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  77. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  78. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  79. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT.
  80.  
  81. '''
  82. from __future__ import generators
  83. __author__ = 'Leonard Richardson (leonardr@segfault.org)'
  84. __version__ = '3.1.0.1'
  85. __copyright__ = 'Copyright (c) 2004-2009 Leonard Richardson'
  86. __license__ = 'New-style BSD'
  87. import codecs
  88. import markupbase
  89. import types
  90. import re
  91. from HTMLParser import HTMLParser, HTMLParseError
  92.  
  93. try:
  94.     from htmlentitydefs import name2codepoint
  95. except ImportError:
  96.     name2codepoint = { }
  97.  
  98.  
  99. try:
  100.     set
  101. except NameError:
  102.     from sets import Set as set
  103.  
  104. markupbase._declname_match = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*\\s*').match
  105. DEFAULT_OUTPUT_ENCODING = 'utf-8'
  106.  
  107. def sob(unicode, encoding):
  108.     '''Returns either the given Unicode string or its encoding.'''
  109.     if encoding is None:
  110.         return unicode
  111.     return unicode.encode(encoding)
  112.  
  113.  
  114. class PageElement:
  115.     '''Contains the navigational information for some part of the page
  116.     (either a tag or a piece of text)'''
  117.     
  118.     def setup(self, parent = None, previous = None):
  119.         '''Sets up the initial relations between this element and
  120.         other elements.'''
  121.         self.parent = parent
  122.         self.previous = previous
  123.         self.next = None
  124.         self.previousSibling = None
  125.         self.nextSibling = None
  126.         if self.parent and self.parent.contents:
  127.             self.previousSibling = self.parent.contents[-1]
  128.             self.previousSibling.nextSibling = self
  129.         
  130.  
  131.     
  132.     def replaceWith(self, replaceWith):
  133.         oldParent = self.parent
  134.         myIndex = self.parent.contents.index(self)
  135.         if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
  136.             index = self.parent.contents.index(replaceWith)
  137.             if index and index < myIndex:
  138.                 myIndex = myIndex - 1
  139.             
  140.         
  141.         self.extract()
  142.         oldParent.insert(myIndex, replaceWith)
  143.  
  144.     
  145.     def extract(self):
  146.         '''Destructively rips this element out of the tree.'''
  147.         if self.parent:
  148.             
  149.             try:
  150.                 self.parent.contents.remove(self)
  151.             except ValueError:
  152.                 pass
  153.             except:
  154.                 None<EXCEPTION MATCH>ValueError
  155.             
  156.  
  157.         None<EXCEPTION MATCH>ValueError
  158.         lastChild = self._lastRecursiveChild()
  159.         nextElement = lastChild.next
  160.         if self.previous:
  161.             self.previous.next = nextElement
  162.         
  163.         if nextElement:
  164.             nextElement.previous = self.previous
  165.         
  166.         self.previous = None
  167.         lastChild.next = None
  168.         self.parent = None
  169.         if self.previousSibling:
  170.             self.previousSibling.nextSibling = self.nextSibling
  171.         
  172.         if self.nextSibling:
  173.             self.nextSibling.previousSibling = self.previousSibling
  174.         
  175.         self.previousSibling = None
  176.         self.nextSibling = None
  177.         return self
  178.  
  179.     
  180.     def _lastRecursiveChild(self):
  181.         '''Finds the last element beneath this object to be parsed.'''
  182.         lastChild = self
  183.         while hasattr(lastChild, 'contents') and lastChild.contents:
  184.             lastChild = lastChild.contents[-1]
  185.         return lastChild
  186.  
  187.     
  188.     def insert(self, position, newChild):
  189.         if (isinstance(newChild, basestring) or isinstance(newChild, unicode)) and not isinstance(newChild, NavigableString):
  190.             newChild = NavigableString(newChild)
  191.         
  192.         position = min(position, len(self.contents))
  193.         if hasattr(newChild, 'parent') and newChild.parent != None:
  194.             if newChild.parent == self:
  195.                 index = self.find(newChild)
  196.                 if index and index < position:
  197.                     position = position - 1
  198.                 
  199.             
  200.             newChild.extract()
  201.         
  202.         newChild.parent = self
  203.         previousChild = None
  204.         if position == 0:
  205.             newChild.previousSibling = None
  206.             newChild.previous = self
  207.         else:
  208.             previousChild = self.contents[position - 1]
  209.             newChild.previousSibling = previousChild
  210.             newChild.previousSibling.nextSibling = newChild
  211.             newChild.previous = previousChild._lastRecursiveChild()
  212.         if newChild.previous:
  213.             newChild.previous.next = newChild
  214.         
  215.         newChildsLastElement = newChild._lastRecursiveChild()
  216.         if position >= len(self.contents):
  217.             newChild.nextSibling = None
  218.             parent = self
  219.             parentsNextSibling = None
  220.             while not parentsNextSibling:
  221.                 parentsNextSibling = parent.nextSibling
  222.                 parent = parent.parent
  223.                 if not parent:
  224.                     break
  225.                     continue
  226.             if parentsNextSibling:
  227.                 newChildsLastElement.next = parentsNextSibling
  228.             else:
  229.                 newChildsLastElement.next = None
  230.         else:
  231.             nextChild = self.contents[position]
  232.             newChild.nextSibling = nextChild
  233.             if newChild.nextSibling:
  234.                 newChild.nextSibling.previousSibling = newChild
  235.             
  236.             newChildsLastElement.next = nextChild
  237.         if newChildsLastElement.next:
  238.             newChildsLastElement.next.previous = newChildsLastElement
  239.         
  240.         self.contents.insert(position, newChild)
  241.  
  242.     
  243.     def append(self, tag):
  244.         '''Appends the given tag to the contents of this tag.'''
  245.         self.insert(len(self.contents), tag)
  246.  
  247.     
  248.     def findNext(self, name = None, attrs = { }, text = None, **kwargs):
  249.         '''Returns the first item that matches the given criteria and
  250.         appears after this Tag in the document.'''
  251.         return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
  252.  
  253.     
  254.     def findAllNext(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  255.         '''Returns all items that match the given criteria and appear
  256.         after this Tag in the document.'''
  257.         return self._findAll(name, attrs, text, limit, self.nextGenerator, **kwargs)
  258.  
  259.     
  260.     def findNextSibling(self, name = None, attrs = { }, text = None, **kwargs):
  261.         '''Returns the closest sibling to this Tag that matches the
  262.         given criteria and appears after this Tag in the document.'''
  263.         return self._findOne(self.findNextSiblings, name, attrs, text, **kwargs)
  264.  
  265.     
  266.     def findNextSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  267.         '''Returns the siblings of this Tag that match the given
  268.         criteria and appear after this Tag in the document.'''
  269.         return self._findAll(name, attrs, text, limit, self.nextSiblingGenerator, **kwargs)
  270.  
  271.     fetchNextSiblings = findNextSiblings
  272.     
  273.     def findPrevious(self, name = None, attrs = { }, text = None, **kwargs):
  274.         '''Returns the first item that matches the given criteria and
  275.         appears before this Tag in the document.'''
  276.         return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
  277.  
  278.     
  279.     def findAllPrevious(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  280.         '''Returns all items that match the given criteria and appear
  281.         before this Tag in the document.'''
  282.         return self._findAll(name, attrs, text, limit, self.previousGenerator, **kwargs)
  283.  
  284.     fetchPrevious = findAllPrevious
  285.     
  286.     def findPreviousSibling(self, name = None, attrs = { }, text = None, **kwargs):
  287.         '''Returns the closest sibling to this Tag that matches the
  288.         given criteria and appears before this Tag in the document.'''
  289.         return self._findOne(self.findPreviousSiblings, name, attrs, text, **kwargs)
  290.  
  291.     
  292.     def findPreviousSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  293.         '''Returns the siblings of this Tag that match the given
  294.         criteria and appear before this Tag in the document.'''
  295.         return self._findAll(name, attrs, text, limit, self.previousSiblingGenerator, **kwargs)
  296.  
  297.     fetchPreviousSiblings = findPreviousSiblings
  298.     
  299.     def findParent(self, name = None, attrs = { }, **kwargs):
  300.         '''Returns the closest parent of this Tag that matches the given
  301.         criteria.'''
  302.         r = None
  303.         l = self.findParents(name, attrs, 1)
  304.         if l:
  305.             r = l[0]
  306.         
  307.         return r
  308.  
  309.     
  310.     def findParents(self, name = None, attrs = { }, limit = None, **kwargs):
  311.         '''Returns the parents of this Tag that match the given
  312.         criteria.'''
  313.         return self._findAll(name, attrs, None, limit, self.parentGenerator, **kwargs)
  314.  
  315.     fetchParents = findParents
  316.     
  317.     def _findOne(self, method, name, attrs, text, **kwargs):
  318.         r = None
  319.         l = method(name, attrs, text, 1, **kwargs)
  320.         if l:
  321.             r = l[0]
  322.         
  323.         return r
  324.  
  325.     
  326.     def _findAll(self, name, attrs, text, limit, generator, **kwargs):
  327.         '''Iterates over a generator looking for things that match.'''
  328.         if isinstance(name, SoupStrainer):
  329.             strainer = name
  330.         else:
  331.             strainer = SoupStrainer(name, attrs, text, **kwargs)
  332.         results = ResultSet(strainer)
  333.         g = generator()
  334.         while True:
  335.             
  336.             try:
  337.                 i = g.next()
  338.             except StopIteration:
  339.                 break
  340.  
  341.             if i:
  342.                 found = strainer.search(i)
  343.                 if found:
  344.                     results.append(found)
  345.                     if limit and len(results) >= limit:
  346.                         break
  347.                     
  348.                 
  349.             found
  350.         return results
  351.  
  352.     
  353.     def nextGenerator(self):
  354.         i = self
  355.         while i:
  356.             i = i.next
  357.             yield i
  358.  
  359.     
  360.     def nextSiblingGenerator(self):
  361.         i = self
  362.         while i:
  363.             i = i.nextSibling
  364.             yield i
  365.  
  366.     
  367.     def previousGenerator(self):
  368.         i = self
  369.         while i:
  370.             i = i.previous
  371.             yield i
  372.  
  373.     
  374.     def previousSiblingGenerator(self):
  375.         i = self
  376.         while i:
  377.             i = i.previousSibling
  378.             yield i
  379.  
  380.     
  381.     def parentGenerator(self):
  382.         i = self
  383.         while i:
  384.             i = i.parent
  385.             yield i
  386.  
  387.     
  388.     def substituteEncoding(self, str, encoding = None):
  389.         if not encoding:
  390.             pass
  391.         encoding = 'utf-8'
  392.         return str.replace('%SOUP-ENCODING%', encoding)
  393.  
  394.     
  395.     def toEncoding(self, s, encoding = None):
  396.         '''Encodes an object to a string in some encoding, or to Unicode.
  397.         .'''
  398.         if isinstance(s, unicode):
  399.             if encoding:
  400.                 s = s.encode(encoding)
  401.             
  402.         elif isinstance(s, str):
  403.             if encoding:
  404.                 s = s.encode(encoding)
  405.             else:
  406.                 s = unicode(s)
  407.         elif encoding:
  408.             s = self.toEncoding(str(s), encoding)
  409.         else:
  410.             s = unicode(s)
  411.         return s
  412.  
  413.  
  414.  
  415. class NavigableString(unicode, PageElement):
  416.     
  417.     def __new__(cls, value):
  418.         """Create a new NavigableString.
  419.  
  420.         When unpickling a NavigableString, this method is called with
  421.         the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be
  422.         passed in to the superclass's __new__ or the superclass won't know
  423.         how to handle non-ASCII characters.
  424.         """
  425.         if isinstance(value, unicode):
  426.             return unicode.__new__(cls, value)
  427.         return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
  428.  
  429.     
  430.     def __getnewargs__(self):
  431.         return (unicode(self),)
  432.  
  433.     
  434.     def __getattr__(self, attr):
  435.         '''text.string gives you text. This is for backwards
  436.         compatibility for Navigable*String, but for CData* it lets you
  437.         get the string without the CData wrapper.'''
  438.         if attr == 'string':
  439.             return self
  440.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
  441.  
  442.     
  443.     def encode(self, encoding = DEFAULT_OUTPUT_ENCODING):
  444.         return self.decode().encode(encoding)
  445.  
  446.     
  447.     def decodeGivenEventualEncoding(self, eventualEncoding):
  448.         return self
  449.  
  450.  
  451.  
  452. class CData(NavigableString):
  453.     
  454.     def decodeGivenEventualEncoding(self, eventualEncoding):
  455.         return u'<![CDATA[' + self + u']]>'
  456.  
  457.  
  458.  
  459. class ProcessingInstruction(NavigableString):
  460.     
  461.     def decodeGivenEventualEncoding(self, eventualEncoding):
  462.         output = self
  463.         if u'%SOUP-ENCODING%' in output:
  464.             output = self.substituteEncoding(output, eventualEncoding)
  465.         
  466.         return u'<?' + output + u'?>'
  467.  
  468.  
  469.  
  470. class Comment(NavigableString):
  471.     
  472.     def decodeGivenEventualEncoding(self, eventualEncoding):
  473.         return u'<!--' + self + u'-->'
  474.  
  475.  
  476.  
  477. class Declaration(NavigableString):
  478.     
  479.     def decodeGivenEventualEncoding(self, eventualEncoding):
  480.         return u'<!' + self + u'>'
  481.  
  482.  
  483.  
  484. class Tag(PageElement):
  485.     '''Represents a found HTML tag with its attributes and contents.'''
  486.     
  487.     def _invert(h):
  488.         '''Cheap function to invert a hash.'''
  489.         i = { }
  490.         for k, v in h.items():
  491.             i[v] = k
  492.         
  493.         return i
  494.  
  495.     XML_ENTITIES_TO_SPECIAL_CHARS = {
  496.         'apos': "'",
  497.         'quot': '"',
  498.         'amp': '&',
  499.         'lt': '<',
  500.         'gt': '>' }
  501.     XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
  502.     
  503.     def _convertEntities(self, match):
  504.         '''Used in a call to re.sub to replace HTML, XML, and numeric
  505.         entities with the appropriate Unicode characters. If HTML
  506.         entities are being converted, any unrecognized entities are
  507.         escaped.'''
  508.         x = match.group(1)
  509.         if self.convertHTMLEntities and x in name2codepoint:
  510.             return unichr(name2codepoint[x])
  511.         if x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
  512.             if self.convertXMLEntities:
  513.                 return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
  514.             return u'&%s;' % x
  515.         x in self.XML_ENTITIES_TO_SPECIAL_CHARS
  516.         if len(x) > 0 and x[0] == '#':
  517.             if len(x) > 1 and x[1] == 'x':
  518.                 return unichr(int(x[2:], 16))
  519.             return unichr(int(x[1:]))
  520.         x[0] == '#'
  521.         if self.escapeUnrecognizedEntities:
  522.             return u'&%s;' % x
  523.         return u'&%s;' % x
  524.  
  525.     
  526.     def __init__(self, parser, name, attrs = None, parent = None, previous = None):
  527.         '''Basic constructor.'''
  528.         self.parserClass = parser.__class__
  529.         self.isSelfClosing = parser.isSelfClosingTag(name)
  530.         self.name = name
  531.         if attrs == None:
  532.             attrs = []
  533.         
  534.         self.attrs = attrs
  535.         self.contents = []
  536.         self.setup(parent, previous)
  537.         self.hidden = False
  538.         self.containsSubstitutions = False
  539.         self.convertHTMLEntities = parser.convertHTMLEntities
  540.         self.convertXMLEntities = parser.convertXMLEntities
  541.         self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
  542.         
  543.         def convert(kval):
  544.             '''Converts HTML, XML and numeric entities in the attribute value.'''
  545.             (k, val) = kval
  546.             if val is None:
  547.                 return kval
  548.             return (k, re.sub('&(#\\d+|#x[0-9a-fA-F]+|\\w+);', self._convertEntities, val))
  549.  
  550.         self.attrs = map(convert, self.attrs)
  551.  
  552.     
  553.     def get(self, key, default = None):
  554.         """Returns the value of the 'key' attribute for the tag, or
  555.         the value given for 'default' if it doesn't have that
  556.         attribute."""
  557.         return self._getAttrMap().get(key, default)
  558.  
  559.     
  560.     def has_key(self, key):
  561.         return self._getAttrMap().has_key(key)
  562.  
  563.     
  564.     def __getitem__(self, key):
  565.         """tag[key] returns the value of the 'key' attribute for the tag,
  566.         and throws an exception if it's not there."""
  567.         return self._getAttrMap()[key]
  568.  
  569.     
  570.     def __iter__(self):
  571.         '''Iterating over a tag iterates over its contents.'''
  572.         return iter(self.contents)
  573.  
  574.     
  575.     def __len__(self):
  576.         '''The length of a tag is the length of its list of contents.'''
  577.         return len(self.contents)
  578.  
  579.     
  580.     def __contains__(self, x):
  581.         return x in self.contents
  582.  
  583.     
  584.     def __nonzero__(self):
  585.         '''A tag is non-None even if it has no contents.'''
  586.         return True
  587.  
  588.     
  589.     def __setitem__(self, key, value):
  590.         """Setting tag[key] sets the value of the 'key' attribute for the
  591.         tag."""
  592.         self._getAttrMap()
  593.         self.attrMap[key] = value
  594.         found = False
  595.         for i in range(0, len(self.attrs)):
  596.             if self.attrs[i][0] == key:
  597.                 self.attrs[i] = (key, value)
  598.                 found = True
  599.                 continue
  600.         
  601.         if not found:
  602.             self.attrs.append((key, value))
  603.         
  604.         self._getAttrMap()[key] = value
  605.  
  606.     
  607.     def __delitem__(self, key):
  608.         """Deleting tag[key] deletes all 'key' attributes for the tag."""
  609.         for item in self.attrs:
  610.             if item[0] == key:
  611.                 self.attrs.remove(item)
  612.             
  613.             self._getAttrMap()
  614.             if self.attrMap.has_key(key):
  615.                 del self.attrMap[key]
  616.                 continue
  617.         
  618.  
  619.     
  620.     def __call__(self, *args, **kwargs):
  621.         """Calling a tag like a function is the same as calling its
  622.         findAll() method. Eg. tag('a') returns a list of all the A tags
  623.         found within this tag."""
  624.         return apply(self.findAll, args, kwargs)
  625.  
  626.     
  627.     def __getattr__(self, tag):
  628.         if len(tag) > 3 and tag.rfind('Tag') == len(tag) - 3:
  629.             return self.find(tag[:-3])
  630.         if tag.find('__') != 0:
  631.             return self.find(tag)
  632.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
  633.  
  634.     
  635.     def __eq__(self, other):
  636.         '''Returns true iff this tag has the same name, the same attributes,
  637.         and the same contents (recursively) as the given tag.
  638.  
  639.         NOTE: right now this will return false if two tags have the
  640.         same attributes in a different order. Should this be fixed?'''
  641.         if not hasattr(other, 'name') and not hasattr(other, 'attrs') and not hasattr(other, 'contents') and self.name != other.name and self.attrs != other.attrs or len(self) != len(other):
  642.             return False
  643.         for i in range(0, len(self.contents)):
  644.             if self.contents[i] != other.contents[i]:
  645.                 return False
  646.         
  647.         return True
  648.  
  649.     
  650.     def __ne__(self, other):
  651.         '''Returns true iff this tag is not identical to the other tag,
  652.         as defined in __eq__.'''
  653.         return not (self == other)
  654.  
  655.     
  656.     def __repr__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  657.         '''Renders this tag as a string.'''
  658.         return self.decode(eventualEncoding = encoding)
  659.  
  660.     BARE_AMPERSAND_OR_BRACKET = re.compile('([<>]|' + '&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)' + ')')
  661.     
  662.     def _sub_entity(self, x):
  663.         '''Used with a regular expression to substitute the
  664.         appropriate XML entity for an XML special character.'''
  665.         return '&' + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ';'
  666.  
  667.     
  668.     def __unicode__(self):
  669.         return self.decode()
  670.  
  671.     
  672.     def __str__(self):
  673.         return self.encode()
  674.  
  675.     
  676.     def encode(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  677.         return self.decode(prettyPrint, indentLevel, encoding).encode(encoding)
  678.  
  679.     
  680.     def decode(self, prettyPrint = False, indentLevel = 0, eventualEncoding = DEFAULT_OUTPUT_ENCODING):
  681.         '''Returns a string or Unicode representation of this tag and
  682.         its contents. To get Unicode, pass None for encoding.'''
  683.         attrs = []
  684.         if self.attrs:
  685.             for key, val in self.attrs:
  686.                 fmt = '%s="%s"'
  687.                 if isString(val):
  688.                     if self.containsSubstitutions and eventualEncoding is not None and '%SOUP-ENCODING%' in val:
  689.                         val = self.substituteEncoding(val, eventualEncoding)
  690.                     
  691.                     if '"' in val:
  692.                         fmt = "%s='%s'"
  693.                         if "'" in val:
  694.                             val = val.replace("'", '&squot;')
  695.                         
  696.                     
  697.                     val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
  698.                 
  699.                 if val is None:
  700.                     decoded = key
  701.                 else:
  702.                     decoded = fmt % (key, val)
  703.                 attrs.append(decoded)
  704.             
  705.         
  706.         close = ''
  707.         closeTag = ''
  708.         if self.isSelfClosing:
  709.             close = ' /'
  710.         else:
  711.             closeTag = '</%s>' % self.name
  712.         (indentTag, indentContents) = (0, 0)
  713.         if prettyPrint:
  714.             indentTag = indentLevel
  715.             space = ' ' * (indentTag - 1)
  716.             indentContents = indentTag + 1
  717.         
  718.         contents = self.decodeContents(prettyPrint, indentContents, eventualEncoding)
  719.         if self.hidden:
  720.             s = contents
  721.         else:
  722.             s = []
  723.             attributeString = ''
  724.             if attrs:
  725.                 attributeString = ' ' + ' '.join(attrs)
  726.             
  727.             if prettyPrint:
  728.                 s.append(space)
  729.             
  730.             s.append('<%s%s%s>' % (self.name, attributeString, close))
  731.             if prettyPrint:
  732.                 s.append('\n')
  733.             
  734.             s.append(contents)
  735.             if prettyPrint and contents and contents[-1] != '\n':
  736.                 s.append('\n')
  737.             
  738.             if prettyPrint and closeTag:
  739.                 s.append(space)
  740.             
  741.             s.append(closeTag)
  742.             if prettyPrint and closeTag and self.nextSibling:
  743.                 s.append('\n')
  744.             
  745.             s = ''.join(s)
  746.         return s
  747.  
  748.     
  749.     def decompose(self):
  750.         '''Recursively destroys the contents of this tree.'''
  751.         contents = [ i for i in self.contents ]
  752.         for i in contents:
  753.             if isinstance(i, Tag):
  754.                 i.decompose()
  755.                 continue
  756.             []
  757.             i.extract()
  758.         
  759.         self.extract()
  760.  
  761.     
  762.     def prettify(self, encoding = DEFAULT_OUTPUT_ENCODING):
  763.         return self.encode(encoding, True)
  764.  
  765.     
  766.     def encodeContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  767.         return self.decodeContents(prettyPrint, indentLevel).encode(encoding)
  768.  
  769.     
  770.     def decodeContents(self, prettyPrint = False, indentLevel = 0, eventualEncoding = DEFAULT_OUTPUT_ENCODING):
  771.         '''Renders the contents of this tag as a string in the given
  772.         encoding. If encoding is None, returns a Unicode string..'''
  773.         s = []
  774.         for c in self:
  775.             text = None
  776.             if isinstance(c, NavigableString):
  777.                 text = c.decodeGivenEventualEncoding(eventualEncoding)
  778.             elif isinstance(c, Tag):
  779.                 s.append(c.decode(prettyPrint, indentLevel, eventualEncoding))
  780.             
  781.             if text and prettyPrint:
  782.                 text = text.strip()
  783.             
  784.             if text:
  785.                 if prettyPrint:
  786.                     s.append(' ' * (indentLevel - 1))
  787.                 
  788.                 s.append(text)
  789.                 if prettyPrint:
  790.                     s.append('\n')
  791.                 
  792.             prettyPrint
  793.         
  794.         return ''.join(s)
  795.  
  796.     
  797.     def find(self, name = None, attrs = { }, recursive = True, text = None, **kwargs):
  798.         '''Return only the first child of this Tag matching the given
  799.         criteria.'''
  800.         r = None
  801.         l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
  802.         if l:
  803.             r = l[0]
  804.         
  805.         return r
  806.  
  807.     findChild = find
  808.     
  809.     def findAll(self, name = None, attrs = { }, recursive = True, text = None, limit = None, **kwargs):
  810.         """Extracts a list of Tag objects that match the given
  811.         criteria.  You can specify the name of the Tag and any
  812.         attributes you want the Tag to have.
  813.  
  814.         The value of a key-value pair in the 'attrs' map can be a
  815.         string, a list of strings, a regular expression object, or a
  816.         callable that takes a string and returns whether or not the
  817.         string matches for some custom definition of 'matches'. The
  818.         same is true of the tag name."""
  819.         generator = self.recursiveChildGenerator
  820.         if not recursive:
  821.             generator = self.childGenerator
  822.         
  823.         return self._findAll(name, attrs, text, limit, generator, **kwargs)
  824.  
  825.     findChildren = findAll
  826.     first = find
  827.     fetch = findAll
  828.     
  829.     def fetchText(self, text = None, recursive = True, limit = None):
  830.         return self.findAll(text = text, recursive = recursive, limit = limit)
  831.  
  832.     
  833.     def firstText(self, text = None, recursive = True):
  834.         return self.find(text = text, recursive = recursive)
  835.  
  836.     
  837.     def renderContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  838.         if encoding is None:
  839.             return self.decodeContents(prettyPrint, indentLevel, encoding)
  840.         return self.encodeContents(encoding, prettyPrint, indentLevel)
  841.  
  842.     
  843.     def _getAttrMap(self):
  844.         """Initializes a map representation of this tag's attributes,
  845.         if not already initialized."""
  846.         if not getattr(self, 'attrMap'):
  847.             self.attrMap = { }
  848.             for key, value in self.attrs:
  849.                 self.attrMap[key] = value
  850.             
  851.         
  852.         return self.attrMap
  853.  
  854.     
  855.     def recursiveChildGenerator(self):
  856.         if not len(self.contents):
  857.             raise StopIteration
  858.         len(self.contents)
  859.         stopNode = self._lastRecursiveChild().next
  860.         current = self.contents[0]
  861.         while current is not stopNode:
  862.             yield current
  863.             current = current.next
  864.  
  865.     
  866.     def childGenerator(self):
  867.         if not len(self.contents):
  868.             raise StopIteration
  869.         len(self.contents)
  870.         current = self.contents[0]
  871.         while current:
  872.             yield current
  873.             current = current.nextSibling
  874.         raise StopIteration
  875.  
  876.  
  877.  
  878. class SoupStrainer:
  879.     '''Encapsulates a number of ways of matching a markup element (tag or
  880.     text).'''
  881.     
  882.     def __init__(self, name = None, attrs = { }, text = None, **kwargs):
  883.         self.name = name
  884.         if isString(attrs):
  885.             kwargs['class'] = attrs
  886.             attrs = None
  887.         
  888.         if kwargs:
  889.             if attrs:
  890.                 attrs = attrs.copy()
  891.                 attrs.update(kwargs)
  892.             else:
  893.                 attrs = kwargs
  894.         
  895.         self.attrs = attrs
  896.         self.text = text
  897.  
  898.     
  899.     def __str__(self):
  900.         if self.text:
  901.             return self.text
  902.         return '%s|%s' % (self.name, self.attrs)
  903.  
  904.     
  905.     def searchTag(self, markupName = None, markupAttrs = { }):
  906.         found = None
  907.         markup = None
  908.         if isinstance(markupName, Tag):
  909.             markup = markupName
  910.             markupAttrs = markup
  911.         
  912.         if callable(self.name):
  913.             pass
  914.         callFunctionWithTagData = not isinstance(markupName, Tag)
  915.         if not not (self.name) and callFunctionWithTagData:
  916.             if (markup or self._matches(markup, self.name) or not markup) and self._matches(markupName, self.name):
  917.                 if callFunctionWithTagData:
  918.                     match = self.name(markupName, markupAttrs)
  919.                 else:
  920.                     match = True
  921.                     markupAttrMap = None
  922.                     for attr, matchAgainst in self.attrs.items():
  923.                         if not markupAttrMap:
  924.                             if hasattr(markupAttrs, 'get'):
  925.                                 markupAttrMap = markupAttrs
  926.                             else:
  927.                                 markupAttrMap = { }
  928.                                 for k, v in markupAttrs:
  929.                                     markupAttrMap[k] = v
  930.                                 
  931.                         
  932.                         attrValue = markupAttrMap.get(attr)
  933.                         if not self._matches(attrValue, matchAgainst):
  934.                             match = False
  935.                             break
  936.                             continue
  937.                     
  938.                 if match:
  939.                     if markup:
  940.                         found = markup
  941.                     else:
  942.                         found = markupName
  943.                 
  944.             
  945.         return found
  946.  
  947.     
  948.     def search(self, markup):
  949.         found = None
  950.         if isList(markup) and not isinstance(markup, Tag):
  951.             for element in markup:
  952.                 if isinstance(element, NavigableString) and self.search(element):
  953.                     found = element
  954.                     break
  955.                     continue
  956.             
  957.         elif isinstance(markup, Tag):
  958.             if not self.text:
  959.                 found = self.searchTag(markup)
  960.             
  961.         elif isinstance(markup, NavigableString) or isString(markup):
  962.             if self._matches(markup, self.text):
  963.                 found = markup
  964.             
  965.         else:
  966.             raise Exception, "I don't know how to match against a %s" % markup.__class__
  967.         return isString(markup)
  968.  
  969.     
  970.     def _matches(self, markup, matchAgainst):
  971.         result = False
  972.         if matchAgainst == True and type(matchAgainst) == types.BooleanType:
  973.             result = markup != None
  974.         elif callable(matchAgainst):
  975.             result = matchAgainst(markup)
  976.         elif isinstance(markup, Tag):
  977.             markup = markup.name
  978.         
  979.         if markup is not None and not isString(markup):
  980.             markup = unicode(markup)
  981.         
  982.         if hasattr(matchAgainst, 'match'):
  983.             if markup:
  984.                 pass
  985.             result = matchAgainst.search(markup)
  986.         elif isList(matchAgainst):
  987.             if markup is not None or not isString(matchAgainst):
  988.                 result = markup in matchAgainst
  989.             elif hasattr(matchAgainst, 'items'):
  990.                 result = markup.has_key(matchAgainst)
  991.             elif matchAgainst and isString(markup):
  992.                 if isinstance(markup, unicode):
  993.                     matchAgainst = unicode(matchAgainst)
  994.                 else:
  995.                     matchAgainst = str(matchAgainst)
  996.             
  997.         if not result:
  998.             result = matchAgainst == markup
  999.         
  1000.         return result
  1001.  
  1002.  
  1003.  
  1004. class ResultSet(list):
  1005.     '''A ResultSet is just a list that keeps track of the SoupStrainer
  1006.     that created it.'''
  1007.     
  1008.     def __init__(self, source):
  1009.         list.__init__([])
  1010.         self.source = source
  1011.  
  1012.  
  1013.  
  1014. def isList(l):
  1015.     '''Convenience method that works with all 2.x versions of Python
  1016.     to determine whether or not something is listlike.'''
  1017.     if not hasattr(l, '__iter__') or not isString(l):
  1018.         pass
  1019.     return type(l) in (types.ListType, types.TupleType)
  1020.  
  1021.  
  1022. def isString(s):
  1023.     '''Convenience method that works with all 2.x versions of Python
  1024.     to determine whether or not something is stringlike.'''
  1025.     
  1026.     try:
  1027.         if not isinstance(s, unicode):
  1028.             pass
  1029.         return isinstance(s, basestring)
  1030.     except NameError:
  1031.         return isinstance(s, str)
  1032.  
  1033.  
  1034.  
  1035. def buildTagMap(default, *args):
  1036.     '''Turns a list of maps, lists, or scalars into a single map.
  1037.     Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and
  1038.     NESTING_RESET_TAGS maps out of lists and partial maps.'''
  1039.     built = { }
  1040.     for portion in args:
  1041.         if hasattr(portion, 'items'):
  1042.             for k, v in portion.items():
  1043.                 built[k] = v
  1044.             
  1045.         if isList(portion) and not isString(portion):
  1046.             for k in portion:
  1047.                 built[k] = default
  1048.             
  1049.         built[portion] = default
  1050.     
  1051.     return built
  1052.  
  1053.  
  1054. class HTMLParserBuilder(HTMLParser):
  1055.     
  1056.     def __init__(self, soup):
  1057.         HTMLParser.__init__(self)
  1058.         self.soup = soup
  1059.  
  1060.     
  1061.     def handle_starttag(self, name, attrs):
  1062.         if name == 'meta':
  1063.             self.soup.extractCharsetFromMeta(attrs)
  1064.         else:
  1065.             self.soup.unknown_starttag(name, attrs)
  1066.  
  1067.     
  1068.     def handle_endtag(self, name):
  1069.         self.soup.unknown_endtag(name)
  1070.  
  1071.     
  1072.     def handle_data(self, content):
  1073.         self.soup.handle_data(content)
  1074.  
  1075.     
  1076.     def _toStringSubclass(self, text, subclass):
  1077.         '''Adds a certain piece of text to the tree as a NavigableString
  1078.         subclass.'''
  1079.         self.soup.endData()
  1080.         self.handle_data(text)
  1081.         self.soup.endData(subclass)
  1082.  
  1083.     
  1084.     def handle_pi(self, text):
  1085.         '''Handle a processing instruction as a ProcessingInstruction
  1086.         object, possibly one with a %SOUP-ENCODING% slot into which an
  1087.         encoding will be plugged later.'''
  1088.         if text[:3] == 'xml':
  1089.             text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
  1090.         
  1091.         self._toStringSubclass(text, ProcessingInstruction)
  1092.  
  1093.     
  1094.     def handle_comment(self, text):
  1095.         '''Handle comments as Comment objects.'''
  1096.         self._toStringSubclass(text, Comment)
  1097.  
  1098.     
  1099.     def handle_charref(self, ref):
  1100.         '''Handle character references as data.'''
  1101.         if self.soup.convertEntities:
  1102.             data = unichr(int(ref))
  1103.         else:
  1104.             data = '&#%s;' % ref
  1105.         self.handle_data(data)
  1106.  
  1107.     
  1108.     def handle_entityref(self, ref):
  1109.         '''Handle entity references as data, possibly converting known
  1110.         HTML and/or XML entity references to the corresponding Unicode
  1111.         characters.'''
  1112.         data = None
  1113.         if self.soup.convertHTMLEntities:
  1114.             
  1115.             try:
  1116.                 data = unichr(name2codepoint[ref])
  1117.             except KeyError:
  1118.                 pass
  1119.             except:
  1120.                 None<EXCEPTION MATCH>KeyError
  1121.             
  1122.  
  1123.         None<EXCEPTION MATCH>KeyError
  1124.         if not data and self.soup.convertXMLEntities:
  1125.             data = self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
  1126.         
  1127.         if not data and self.soup.convertHTMLEntities and not self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
  1128.             data = '&%s' % ref
  1129.         
  1130.         if not data:
  1131.             data = '&%s;' % ref
  1132.         
  1133.         self.handle_data(data)
  1134.  
  1135.     
  1136.     def handle_decl(self, data):
  1137.         '''Handle DOCTYPEs and the like as Declaration objects.'''
  1138.         self._toStringSubclass(data, Declaration)
  1139.  
  1140.     
  1141.     def parse_declaration(self, i):
  1142.         '''Treat a bogus SGML declaration as raw data. Treat a CDATA
  1143.         declaration as a CData object.'''
  1144.         j = None
  1145.         if self.rawdata[i:i + 9] == '<![CDATA[':
  1146.             k = self.rawdata.find(']]>', i)
  1147.             if k == -1:
  1148.                 k = len(self.rawdata)
  1149.             
  1150.             data = self.rawdata[i + 9:k]
  1151.             j = k + 3
  1152.             self._toStringSubclass(data, CData)
  1153.         else:
  1154.             
  1155.             try:
  1156.                 j = HTMLParser.parse_declaration(self, i)
  1157.             except HTMLParseError:
  1158.                 toHandle = self.rawdata[i:]
  1159.                 self.handle_data(toHandle)
  1160.                 j = i + len(toHandle)
  1161.  
  1162.         return j
  1163.  
  1164.  
  1165.  
  1166. class BeautifulStoneSoup(Tag):
  1167.     '''This class contains the basic parser and search code. It defines
  1168.     a parser that knows nothing about tag behavior except for the
  1169.     following:
  1170.  
  1171.       You can\'t close a tag without closing all the tags it encloses.
  1172.       That is, "<foo><bar></foo>" actually means
  1173.       "<foo><bar></bar></foo>".
  1174.  
  1175.     [Another possible explanation is "<foo><bar /></foo>", but since
  1176.     this class defines no SELF_CLOSING_TAGS, it will never use that
  1177.     explanation.]
  1178.  
  1179.     This class is useful for parsing XML or made-up markup languages,
  1180.     or when BeautifulSoup makes an assumption counter to what you were
  1181.     expecting.'''
  1182.     SELF_CLOSING_TAGS = { }
  1183.     NESTABLE_TAGS = { }
  1184.     RESET_NESTING_TAGS = { }
  1185.     QUOTE_TAGS = { }
  1186.     PRESERVE_WHITESPACE_TAGS = []
  1187.     MARKUP_MASSAGE = [
  1188.         (re.compile('(<[^<>]*)/>'), (lambda x: x.group(1) + ' />')),
  1189.         (re.compile('<!\\s+([^<>]*)>'), (lambda x: '<!' + x.group(1) + '>'))]
  1190.     ROOT_TAG_NAME = u'[document]'
  1191.     HTML_ENTITIES = 'html'
  1192.     XML_ENTITIES = 'xml'
  1193.     XHTML_ENTITIES = 'xhtml'
  1194.     ALL_ENTITIES = XHTML_ENTITIES
  1195.     STRIP_ASCII_SPACES = {
  1196.         9: None,
  1197.         10: None,
  1198.         12: None,
  1199.         13: None,
  1200.         32: None }
  1201.     
  1202.     def __init__(self, markup = '', parseOnlyThese = None, fromEncoding = None, markupMassage = True, smartQuotesTo = XML_ENTITIES, convertEntities = None, selfClosingTags = None, isHTML = False, builder = HTMLParserBuilder):
  1203.         """The Soup object is initialized as the 'root tag', and the
  1204.         provided markup (which can be a string or a file-like object)
  1205.         is fed into the underlying parser.
  1206.  
  1207.         HTMLParser will process most bad HTML, and the BeautifulSoup
  1208.         class has some tricks for dealing with some HTML that kills
  1209.         HTMLParser, but Beautiful Soup can nonetheless choke or lose data
  1210.         if your data uses self-closing tags or declarations
  1211.         incorrectly.
  1212.  
  1213.         By default, Beautiful Soup uses regexes to sanitize input,
  1214.         avoiding the vast majority of these problems. If the problems
  1215.         don't apply to you, pass in False for markupMassage, and
  1216.         you'll get better performance.
  1217.  
  1218.         The default parser massage techniques fix the two most common
  1219.         instances of invalid HTML that choke HTMLParser:
  1220.  
  1221.          <br/> (No space between name of closing tag and tag close)
  1222.          <! --Comment--> (Extraneous whitespace in declaration)
  1223.  
  1224.         You can pass in a custom list of (RE object, replace method)
  1225.         tuples to get Beautiful Soup to scrub your input the way you
  1226.         want."""
  1227.         self.parseOnlyThese = parseOnlyThese
  1228.         self.fromEncoding = fromEncoding
  1229.         self.smartQuotesTo = smartQuotesTo
  1230.         self.convertEntities = convertEntities
  1231.         if self.convertEntities:
  1232.             self.smartQuotesTo = None
  1233.             if convertEntities == self.HTML_ENTITIES:
  1234.                 self.convertXMLEntities = False
  1235.                 self.convertHTMLEntities = True
  1236.                 self.escapeUnrecognizedEntities = True
  1237.             elif convertEntities == self.XHTML_ENTITIES:
  1238.                 self.convertXMLEntities = True
  1239.                 self.convertHTMLEntities = True
  1240.                 self.escapeUnrecognizedEntities = False
  1241.             elif convertEntities == self.XML_ENTITIES:
  1242.                 self.convertXMLEntities = True
  1243.                 self.convertHTMLEntities = False
  1244.                 self.escapeUnrecognizedEntities = False
  1245.             
  1246.         else:
  1247.             self.convertXMLEntities = False
  1248.             self.convertHTMLEntities = False
  1249.             self.escapeUnrecognizedEntities = False
  1250.         self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
  1251.         self.builder = builder(self)
  1252.         self.reset()
  1253.         if hasattr(markup, 'read'):
  1254.             markup = markup.read()
  1255.         
  1256.         self.markup = markup
  1257.         self.markupMassage = markupMassage
  1258.         
  1259.         try:
  1260.             self._feed(isHTML = isHTML)
  1261.         except StopParsing:
  1262.             pass
  1263.  
  1264.         self.markup = None
  1265.         self.builder = None
  1266.  
  1267.     
  1268.     def _feed(self, inDocumentEncoding = None, isHTML = False):
  1269.         markup = self.markup
  1270.         if isinstance(markup, unicode):
  1271.             if not hasattr(self, 'originalEncoding'):
  1272.                 self.originalEncoding = None
  1273.             
  1274.         else:
  1275.             dammit = UnicodeDammit(markup, [
  1276.                 self.fromEncoding,
  1277.                 inDocumentEncoding], smartQuotesTo = self.smartQuotesTo, isHTML = isHTML)
  1278.             markup = dammit.unicode
  1279.             self.originalEncoding = dammit.originalEncoding
  1280.             self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
  1281.         if markup:
  1282.             if self.markupMassage:
  1283.                 if not isList(self.markupMassage):
  1284.                     self.markupMassage = self.MARKUP_MASSAGE
  1285.                 
  1286.                 for fix, m in self.markupMassage:
  1287.                     markup = fix.sub(m, markup)
  1288.                 
  1289.                 del self.markupMassage
  1290.             
  1291.         
  1292.         self.builder.reset()
  1293.         self.builder.feed(markup)
  1294.         self.endData()
  1295.         while self.currentTag.name != self.ROOT_TAG_NAME:
  1296.             self.popTag()
  1297.  
  1298.     
  1299.     def isSelfClosingTag(self, name):
  1300.         '''Returns true iff the given string is the name of a
  1301.         self-closing tag according to this parser.'''
  1302.         if not self.SELF_CLOSING_TAGS.has_key(name):
  1303.             pass
  1304.         return self.instanceSelfClosingTags.has_key(name)
  1305.  
  1306.     
  1307.     def reset(self):
  1308.         Tag.__init__(self, self, self.ROOT_TAG_NAME)
  1309.         self.hidden = 1
  1310.         self.builder.reset()
  1311.         self.currentData = []
  1312.         self.currentTag = None
  1313.         self.tagStack = []
  1314.         self.quoteStack = []
  1315.         self.pushTag(self)
  1316.  
  1317.     
  1318.     def popTag(self):
  1319.         tag = self.tagStack.pop()
  1320.         if len(self.currentTag.contents) == 1 and isinstance(self.currentTag.contents[0], NavigableString):
  1321.             self.currentTag.string = self.currentTag.contents[0]
  1322.         
  1323.         if self.tagStack:
  1324.             self.currentTag = self.tagStack[-1]
  1325.         
  1326.         return self.currentTag
  1327.  
  1328.     
  1329.     def pushTag(self, tag):
  1330.         if self.currentTag:
  1331.             self.currentTag.contents.append(tag)
  1332.         
  1333.         self.tagStack.append(tag)
  1334.         self.currentTag = self.tagStack[-1]
  1335.  
  1336.     
  1337.     def endData(self, containerClass = NavigableString):
  1338.         if self.currentData:
  1339.             currentData = u''.join(self.currentData)
  1340.             self.currentData = []
  1341.             if self.parseOnlyThese and len(self.tagStack) <= 1:
  1342.                 if not (self.parseOnlyThese.text) or not self.parseOnlyThese.search(currentData):
  1343.                     return None
  1344.                 o = containerClass(currentData)
  1345.                 o.setup(self.currentTag, self.previous)
  1346.             self.previous = o
  1347.             self.currentTag.contents.append(o)
  1348.         
  1349.  
  1350.     
  1351.     def _popToTag(self, name, inclusivePop = True):
  1352.         '''Pops the tag stack up to and including the most recent
  1353.         instance of the given tag. If inclusivePop is false, pops the tag
  1354.         stack up to but *not* including the most recent instqance of
  1355.         the given tag.'''
  1356.         if name == self.ROOT_TAG_NAME:
  1357.             return None
  1358.         numPops = 0
  1359.         mostRecentTag = None
  1360.         for i in range(len(self.tagStack) - 1, 0, -1):
  1361.             if name == self.tagStack[i].name:
  1362.                 numPops = len(self.tagStack) - i
  1363.                 break
  1364.                 continue
  1365.             name == self.ROOT_TAG_NAME
  1366.         
  1367.         if not inclusivePop:
  1368.             numPops = numPops - 1
  1369.         
  1370.         for i in range(0, numPops):
  1371.             mostRecentTag = self.popTag()
  1372.         
  1373.         return mostRecentTag
  1374.  
  1375.     
  1376.     def _smartPop(self, name):
  1377.         """We need to pop up to the previous tag of this type, unless
  1378.         one of this tag's nesting reset triggers comes between this
  1379.         tag and the previous tag of this type, OR unless this tag is a
  1380.         generic nesting trigger and another generic nesting trigger
  1381.         comes between this tag and the previous tag of this type.
  1382.  
  1383.         Examples:
  1384.          <p>Foo<b>Bar *<p>* should pop to 'p', not 'b'.
  1385.          <p>Foo<table>Bar *<p>* should pop to 'table', not 'p'.
  1386.          <p>Foo<table><tr>Bar *<p>* should pop to 'tr', not 'p'.
  1387.  
  1388.          <li><ul><li> *<li>* should pop to 'ul', not the first 'li'.
  1389.          <tr><table><tr> *<tr>* should pop to 'table', not the first 'tr'
  1390.          <td><tr><td> *<td>* should pop to 'tr', not the first 'td'
  1391.         """
  1392.         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
  1393.         isNestable = nestingResetTriggers != None
  1394.         isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
  1395.         popTo = None
  1396.         inclusive = True
  1397.         for i in range(len(self.tagStack) - 1, 0, -1):
  1398.             p = self.tagStack[i]
  1399.             if (not p or p.name == name) and not isNestable:
  1400.                 popTo = name
  1401.                 break
  1402.             
  1403.             if (nestingResetTriggers != None or p.name in nestingResetTriggers or nestingResetTriggers == None) and isResetNesting and self.RESET_NESTING_TAGS.has_key(p.name):
  1404.                 popTo = p.name
  1405.                 inclusive = False
  1406.                 break
  1407.             
  1408.             p = p.parent
  1409.         
  1410.         if popTo:
  1411.             self._popToTag(popTo, inclusive)
  1412.         
  1413.  
  1414.     
  1415.     def unknown_starttag(self, name, attrs, selfClosing = 0):
  1416.         if self.quoteStack:
  1417.             attrs = ''.join(map((lambda .0: (x, y) = .0' %s="%s"' % (x, y)), attrs))
  1418.             self.handle_data('<%s%s>' % (name, attrs))
  1419.             return None
  1420.         self.endData()
  1421.         if not self.isSelfClosingTag(name) and not selfClosing:
  1422.             self._smartPop(name)
  1423.         
  1424.         if self.parseOnlyThese and len(self.tagStack) <= 1:
  1425.             if self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs):
  1426.                 return None
  1427.             tag = Tag(self, name, attrs, self.currentTag, self.previous)
  1428.             if self.previous:
  1429.                 self.previous.next = tag
  1430.             
  1431.         self.previous = tag
  1432.         self.pushTag(tag)
  1433.         if selfClosing or self.isSelfClosingTag(name):
  1434.             self.popTag()
  1435.         
  1436.         if name in self.QUOTE_TAGS:
  1437.             self.quoteStack.append(name)
  1438.             self.literal = 1
  1439.         
  1440.         return tag
  1441.  
  1442.     
  1443.     def unknown_endtag(self, name):
  1444.         if self.quoteStack and self.quoteStack[-1] != name:
  1445.             self.handle_data('</%s>' % name)
  1446.             return None
  1447.         self.endData()
  1448.         self._popToTag(name)
  1449.         if self.quoteStack and self.quoteStack[-1] == name:
  1450.             self.quoteStack.pop()
  1451.             self.literal = len(self.quoteStack) > 0
  1452.         
  1453.  
  1454.     
  1455.     def handle_data(self, data):
  1456.         self.currentData.append(data)
  1457.  
  1458.     
  1459.     def extractCharsetFromMeta(self, attrs):
  1460.         self.unknown_starttag('meta', attrs)
  1461.  
  1462.  
  1463.  
  1464. class BeautifulSoup(BeautifulStoneSoup):
  1465.     """This parser knows the following facts about HTML:
  1466.  
  1467.     * Some tags have no closing tag and should be interpreted as being
  1468.       closed as soon as they are encountered.
  1469.  
  1470.     * The text inside some tags (ie. 'script') may contain tags which
  1471.       are not really part of the document and which should be parsed
  1472.       as text, not tags. If you want to parse the text as tags, you can
  1473.       always fetch it and parse it explicitly.
  1474.  
  1475.     * Tag nesting rules:
  1476.  
  1477.       Most tags can't be nested at all. For instance, the occurance of
  1478.       a <p> tag should implicitly close the previous <p> tag.
  1479.  
  1480.        <p>Para1<p>Para2
  1481.         should be transformed into:
  1482.        <p>Para1</p><p>Para2
  1483.  
  1484.       Some tags can be nested arbitrarily. For instance, the occurance
  1485.       of a <blockquote> tag should _not_ implicitly close the previous
  1486.       <blockquote> tag.
  1487.  
  1488.        Alice said: <blockquote>Bob said: <blockquote>Blah
  1489.         should NOT be transformed into:
  1490.        Alice said: <blockquote>Bob said: </blockquote><blockquote>Blah
  1491.  
  1492.       Some tags can be nested, but the nesting is reset by the
  1493.       interposition of other tags. For instance, a <tr> tag should
  1494.       implicitly close the previous <tr> tag within the same <table>,
  1495.       but not close a <tr> tag in another table.
  1496.  
  1497.        <table><tr>Blah<tr>Blah
  1498.         should be transformed into:
  1499.        <table><tr>Blah</tr><tr>Blah
  1500.         but,
  1501.        <tr>Blah<table><tr>Blah
  1502.         should NOT be transformed into
  1503.        <tr>Blah<table></tr><tr>Blah
  1504.  
  1505.     Differing assumptions about tag nesting rules are a major source
  1506.     of problems with the BeautifulSoup class. If BeautifulSoup is not
  1507.     treating as nestable a tag your page author treats as nestable,
  1508.     try ICantBelieveItsBeautifulSoup, MinimalSoup, or
  1509.     BeautifulStoneSoup before writing your own subclass."""
  1510.     
  1511.     def __init__(self, *args, **kwargs):
  1512.         if not kwargs.has_key('smartQuotesTo'):
  1513.             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
  1514.         
  1515.         kwargs['isHTML'] = True
  1516.         BeautifulStoneSoup.__init__(self, *args, **kwargs)
  1517.  
  1518.     SELF_CLOSING_TAGS = buildTagMap(None, [
  1519.         'br',
  1520.         'hr',
  1521.         'input',
  1522.         'img',
  1523.         'meta',
  1524.         'spacer',
  1525.         'link',
  1526.         'frame',
  1527.         'base'])
  1528.     PRESERVE_WHITESPACE_TAGS = set([
  1529.         'pre',
  1530.         'textarea'])
  1531.     QUOTE_TAGS = {
  1532.         'script': None,
  1533.         'textarea': None }
  1534.     NESTABLE_INLINE_TAGS = [
  1535.         'span',
  1536.         'font',
  1537.         'q',
  1538.         'object',
  1539.         'bdo',
  1540.         'sub',
  1541.         'sup',
  1542.         'center']
  1543.     NESTABLE_BLOCK_TAGS = [
  1544.         'blockquote',
  1545.         'div',
  1546.         'fieldset',
  1547.         'ins',
  1548.         'del']
  1549.     NESTABLE_LIST_TAGS = {
  1550.         'ol': [],
  1551.         'ul': [],
  1552.         'li': [
  1553.             'ul',
  1554.             'ol'],
  1555.         'dl': [],
  1556.         'dd': [
  1557.             'dl'],
  1558.         'dt': [
  1559.             'dl'] }
  1560.     NESTABLE_TABLE_TAGS = {
  1561.         'table': [],
  1562.         'tr': [
  1563.             'table',
  1564.             'tbody',
  1565.             'tfoot',
  1566.             'thead'],
  1567.         'td': [
  1568.             'tr'],
  1569.         'th': [
  1570.             'tr'],
  1571.         'thead': [
  1572.             'table'],
  1573.         'tbody': [
  1574.             'table'],
  1575.         'tfoot': [
  1576.             'table'] }
  1577.     NON_NESTABLE_BLOCK_TAGS = [
  1578.         'address',
  1579.         'form',
  1580.         'p',
  1581.         'pre']
  1582.     RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', NON_NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1583.     NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1584.     CHARSET_RE = re.compile('((^|;)\\s*charset=)([^;]*)', re.M)
  1585.     
  1586.     def extractCharsetFromMeta(self, attrs):
  1587.         '''Beautiful Soup can detect a charset included in a META tag,
  1588.         try to convert the document to that charset, and re-parse the
  1589.         document from the beginning.'''
  1590.         httpEquiv = None
  1591.         contentType = None
  1592.         contentTypeIndex = None
  1593.         tagNeedsEncodingSubstitution = False
  1594.         for i in range(0, len(attrs)):
  1595.             (key, value) = attrs[i]
  1596.             key = key.lower()
  1597.             if key == 'http-equiv':
  1598.                 httpEquiv = value
  1599.                 continue
  1600.             if key == 'content':
  1601.                 contentType = value
  1602.                 contentTypeIndex = i
  1603.                 continue
  1604.         
  1605.         if httpEquiv and contentType:
  1606.             match = self.CHARSET_RE.search(contentType)
  1607.             if match:
  1608.                 if self.declaredHTMLEncoding is not None or self.originalEncoding == self.fromEncoding:
  1609.                     
  1610.                     def rewrite(match):
  1611.                         return match.group(1) + '%SOUP-ENCODING%'
  1612.  
  1613.                     newAttr = self.CHARSET_RE.sub(rewrite, contentType)
  1614.                     attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], newAttr)
  1615.                     tagNeedsEncodingSubstitution = True
  1616.                 else:
  1617.                     newCharset = match.group(3)
  1618.                     if newCharset and newCharset != self.originalEncoding:
  1619.                         self.declaredHTMLEncoding = newCharset
  1620.                         self._feed(self.declaredHTMLEncoding)
  1621.                         raise StopParsing
  1622.                     newCharset != self.originalEncoding
  1623.             
  1624.         
  1625.         tag = self.unknown_starttag('meta', attrs)
  1626.         if tag and tagNeedsEncodingSubstitution:
  1627.             tag.containsSubstitutions = True
  1628.         
  1629.  
  1630.  
  1631.  
  1632. class StopParsing(Exception):
  1633.     pass
  1634.  
  1635.  
  1636. class ICantBelieveItsBeautifulSoup(BeautifulSoup):
  1637.     '''The BeautifulSoup class is oriented towards skipping over
  1638.     common HTML errors like unclosed tags. However, sometimes it makes
  1639.     errors of its own. For instance, consider this fragment:
  1640.  
  1641.      <b>Foo<b>Bar</b></b>
  1642.  
  1643.     This is perfectly valid (if bizarre) HTML. However, the
  1644.     BeautifulSoup class will implicitly close the first b tag when it
  1645.     encounters the second \'b\'. It will think the author wrote
  1646.     "<b>Foo<b>Bar", and didn\'t close the first \'b\' tag, because
  1647.     there\'s no real-world reason to bold something that\'s already
  1648.     bold. When it encounters \'</b></b>\' it will close two more \'b\'
  1649.     tags, for a grand total of three tags closed instead of two. This
  1650.     can throw off the rest of your document structure. The same is
  1651.     true of a number of other tags, listed below.
  1652.  
  1653.     It\'s much more common for someone to forget to close a \'b\' tag
  1654.     than to actually use nested \'b\' tags, and the BeautifulSoup class
  1655.     handles the common case. This class handles the not-co-common
  1656.     case: where you can\'t believe someone wrote what they did, but
  1657.     it\'s valid HTML and BeautifulSoup screwed up by assuming it
  1658.     wouldn\'t be.'''
  1659.     I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = [
  1660.         'em',
  1661.         'big',
  1662.         'i',
  1663.         'small',
  1664.         'tt',
  1665.         'abbr',
  1666.         'acronym',
  1667.         'strong',
  1668.         'cite',
  1669.         'code',
  1670.         'dfn',
  1671.         'kbd',
  1672.         'samp',
  1673.         'strong',
  1674.         'var',
  1675.         'b',
  1676.         'big']
  1677.     I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = [
  1678.         'noscript']
  1679.     NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
  1680.  
  1681.  
  1682. class MinimalSoup(BeautifulSoup):
  1683.     '''The MinimalSoup class is for parsing HTML that contains
  1684.     pathologically bad markup. It makes no assumptions about tag
  1685.     nesting, but it does know which tags are self-closing, that
  1686.     <script> tags contain Javascript and should not be parsed, that
  1687.     META tags may contain encoding information, and so on.
  1688.  
  1689.     This also makes it better for subclassing than BeautifulStoneSoup
  1690.     or BeautifulSoup.'''
  1691.     RESET_NESTING_TAGS = buildTagMap('noscript')
  1692.     NESTABLE_TAGS = { }
  1693.  
  1694.  
  1695. class BeautifulSOAP(BeautifulStoneSoup):
  1696.     '''This class will push a tag with only a single string child into
  1697.     the tag\'s parent as an attribute. The attribute\'s name is the tag
  1698.     name, and the value is the string child. An example should give
  1699.     the flavor of the change:
  1700.  
  1701.     <foo><bar>baz</bar></foo>
  1702.      =>
  1703.     <foo bar="baz"><bar>baz</bar></foo>
  1704.  
  1705.     You can then access fooTag[\'bar\'] instead of fooTag.barTag.string.
  1706.  
  1707.     This is, of course, useful for scraping structures that tend to
  1708.     use subelements instead of attributes, such as SOAP messages. Note
  1709.     that it modifies its input, so don\'t print the modified version
  1710.     out.
  1711.  
  1712.     I\'m not sure how many people really want to use this class; let me
  1713.     know if you do. Mainly I like the name.'''
  1714.     
  1715.     def popTag(self):
  1716.         if len(self.tagStack) > 1:
  1717.             tag = self.tagStack[-1]
  1718.             parent = self.tagStack[-2]
  1719.             parent._getAttrMap()
  1720.             if isinstance(tag, Tag) and len(tag.contents) == 1 and isinstance(tag.contents[0], NavigableString) and not parent.attrMap.has_key(tag.name):
  1721.                 parent[tag.name] = tag.contents[0]
  1722.             
  1723.         
  1724.         BeautifulStoneSoup.popTag(self)
  1725.  
  1726.  
  1727.  
  1728. class RobustXMLParser(BeautifulStoneSoup):
  1729.     pass
  1730.  
  1731.  
  1732. class RobustHTMLParser(BeautifulSoup):
  1733.     pass
  1734.  
  1735.  
  1736. class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
  1737.     pass
  1738.  
  1739.  
  1740. class RobustInsanelyWackAssHTMLParser(MinimalSoup):
  1741.     pass
  1742.  
  1743.  
  1744. class SimplifyingSOAPParser(BeautifulSOAP):
  1745.     pass
  1746.  
  1747.  
  1748. try:
  1749.     import chardet
  1750. except ImportError:
  1751.     chardet = None
  1752.  
  1753.  
  1754. try:
  1755.     import cjkcodecs.aliases as cjkcodecs
  1756. except ImportError:
  1757.     pass
  1758.  
  1759.  
  1760. try:
  1761.     import iconv_codec
  1762. except ImportError:
  1763.     pass
  1764.  
  1765.  
  1766. class UnicodeDammit:
  1767.     '''A class for detecting the encoding of a *ML document and
  1768.     converting it to a Unicode string. If the source encoding is
  1769.     windows-1252, can replace MS smart quotes with their HTML or XML
  1770.     equivalents.'''
  1771.     CHARSET_ALIASES = {
  1772.         'macintosh': 'mac-roman',
  1773.         'x-sjis': 'shift-jis' }
  1774.     
  1775.     def __init__(self, markup, overrideEncodings = [], smartQuotesTo = 'xml', isHTML = False):
  1776.         self.declaredHTMLEncoding = None
  1777.         (self.markup, documentEncoding, sniffedEncoding) = self._detectEncoding(markup, isHTML)
  1778.         self.smartQuotesTo = smartQuotesTo
  1779.         self.triedEncodings = []
  1780.         if markup == '' or isinstance(markup, unicode):
  1781.             self.originalEncoding = None
  1782.             self.unicode = unicode(markup)
  1783.             return None
  1784.         u = None
  1785.         for proposedEncoding in overrideEncodings:
  1786.             u = self._convertFrom(proposedEncoding)
  1787.             if u:
  1788.                 break
  1789.                 continue
  1790.             isinstance(markup, unicode)
  1791.         
  1792.         if not u:
  1793.             for proposedEncoding in (documentEncoding, sniffedEncoding):
  1794.                 u = self._convertFrom(proposedEncoding)
  1795.                 if u:
  1796.                     break
  1797.                     continue
  1798.             
  1799.         
  1800.         if not u and chardet and not isinstance(self.markup, unicode):
  1801.             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
  1802.         
  1803.         if not u:
  1804.             for proposed_encoding in ('utf-8', 'windows-1252'):
  1805.                 u = self._convertFrom(proposed_encoding)
  1806.                 if u:
  1807.                     break
  1808.                     continue
  1809.             
  1810.         
  1811.         self.unicode = u
  1812.         if not u:
  1813.             self.originalEncoding = None
  1814.         
  1815.  
  1816.     
  1817.     def _subMSChar(self, match):
  1818.         '''Changes a MS smart quote character to an XML or HTML
  1819.         entity.'''
  1820.         orig = match.group(1)
  1821.         sub = self.MS_CHARS.get(orig)
  1822.         if type(sub) == types.TupleType:
  1823.             if self.smartQuotesTo == 'xml':
  1824.                 sub = '&#x'.encode() + sub[1].encode() + ';'.encode()
  1825.             else:
  1826.                 sub = '&'.encode() + sub[0].encode() + ';'.encode()
  1827.         else:
  1828.             sub = sub.encode()
  1829.         return sub
  1830.  
  1831.     
  1832.     def _convertFrom(self, proposed):
  1833.         proposed = self.find_codec(proposed)
  1834.         if not proposed or proposed in self.triedEncodings:
  1835.             return None
  1836.         self.triedEncodings.append(proposed)
  1837.         markup = self.markup
  1838.         if self.smartQuotesTo and proposed.lower() in ('windows-1252', 'iso-8859-1', 'iso-8859-2'):
  1839.             smart_quotes_re = '([\x80-\x9f])'
  1840.             smart_quotes_compiled = re.compile(smart_quotes_re)
  1841.             markup = smart_quotes_compiled.sub(self._subMSChar, markup)
  1842.         
  1843.         
  1844.         try:
  1845.             u = self._toUnicode(markup, proposed)
  1846.             self.markup = u
  1847.             self.originalEncoding = proposed
  1848.         except Exception:
  1849.             e = None
  1850.             return None
  1851.  
  1852.         return self.markup
  1853.  
  1854.     
  1855.     def _toUnicode(self, data, encoding):
  1856.         '''Given a string and its encoding, decodes the string into Unicode.
  1857.         %encoding is a string recognized by encodings.aliases'''
  1858.         if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  1859.             encoding = 'utf-16be'
  1860.             data = data[2:]
  1861.         elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  1862.             encoding = 'utf-16le'
  1863.             data = data[2:]
  1864.         elif data[:3] == '\xef\xbb\xbf':
  1865.             encoding = 'utf-8'
  1866.             data = data[3:]
  1867.         elif data[:4] == '\x00\x00\xfe\xff':
  1868.             encoding = 'utf-32be'
  1869.             data = data[4:]
  1870.         elif data[:4] == '\xff\xfe\x00\x00':
  1871.             encoding = 'utf-32le'
  1872.             data = data[4:]
  1873.         
  1874.         newdata = unicode(data, encoding)
  1875.         return newdata
  1876.  
  1877.     
  1878.     def _detectEncoding(self, xml_data, isHTML = False):
  1879.         '''Given a document, tries to detect its XML encoding.'''
  1880.         xml_encoding = None
  1881.         sniffed_xml_encoding = None
  1882.         
  1883.         try:
  1884.             if xml_data[:4] == 'Lo\xa7\x94':
  1885.                 xml_data = self._ebcdic_to_ascii(xml_data)
  1886.             elif xml_data[:4] == '\x00<\x00?':
  1887.                 sniffed_xml_encoding = 'utf-16be'
  1888.                 xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  1889.             elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  1890.                 sniffed_xml_encoding = 'utf-16be'
  1891.                 xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  1892.             elif xml_data[:4] == '<\x00?\x00':
  1893.                 sniffed_xml_encoding = 'utf-16le'
  1894.                 xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  1895.             elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  1896.                 sniffed_xml_encoding = 'utf-16le'
  1897.                 xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  1898.             elif xml_data[:4] == '\x00\x00\x00<':
  1899.                 sniffed_xml_encoding = 'utf-32be'
  1900.                 xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  1901.             elif xml_data[:4] == '<\x00\x00\x00':
  1902.                 sniffed_xml_encoding = 'utf-32le'
  1903.                 xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  1904.             elif xml_data[:4] == '\x00\x00\xfe\xff':
  1905.                 sniffed_xml_encoding = 'utf-32be'
  1906.                 xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  1907.             elif xml_data[:4] == '\xff\xfe\x00\x00':
  1908.                 sniffed_xml_encoding = 'utf-32le'
  1909.                 xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  1910.             elif xml_data[:3] == '\xef\xbb\xbf':
  1911.                 sniffed_xml_encoding = 'utf-8'
  1912.                 xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  1913.             else:
  1914.                 sniffed_xml_encoding = 'ascii'
  1915.         except:
  1916.             xml_encoding_match = None
  1917.  
  1918.         xml_encoding_re = '^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>'.encode()
  1919.         xml_encoding_match = re.compile(xml_encoding_re).match(xml_data)
  1920.         if not xml_encoding_match and isHTML:
  1921.             meta_re = '<\\s*meta[^>]+charset=([^>]*?)[;\'">]'.encode()
  1922.             regexp = re.compile(meta_re, re.I)
  1923.             xml_encoding_match = regexp.search(xml_data)
  1924.         
  1925.         if xml_encoding_match is not None:
  1926.             xml_encoding = xml_encoding_match.groups()[0].decode('ascii').lower()
  1927.             if isHTML:
  1928.                 self.declaredHTMLEncoding = xml_encoding
  1929.             
  1930.             if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  1931.                 xml_encoding = sniffed_xml_encoding
  1932.             
  1933.         
  1934.         return (xml_data, xml_encoding, sniffed_xml_encoding)
  1935.  
  1936.     
  1937.     def find_codec(self, charset):
  1938.         if not self._codec(self.CHARSET_ALIASES.get(charset, charset)):
  1939.             if not charset or self._codec(charset.replace('-', '')):
  1940.                 if not charset or self._codec(charset.replace('-', '_')):
  1941.                     pass
  1942.         return charset
  1943.  
  1944.     
  1945.     def _codec(self, charset):
  1946.         if not charset:
  1947.             return charset
  1948.         codec = None
  1949.         
  1950.         try:
  1951.             codecs.lookup(charset)
  1952.             codec = charset
  1953.         except (LookupError, ValueError):
  1954.             charset
  1955.             charset
  1956.         except:
  1957.             charset
  1958.  
  1959.         return codec
  1960.  
  1961.     EBCDIC_TO_ASCII_MAP = None
  1962.     
  1963.     def _ebcdic_to_ascii(self, s):
  1964.         c = self.__class__
  1965.         if not c.EBCDIC_TO_ASCII_MAP:
  1966.             emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  1967.             import string
  1968.             c.EBCDIC_TO_ASCII_MAP = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  1969.         
  1970.         return s.translate(c.EBCDIC_TO_ASCII_MAP)
  1971.  
  1972.     MS_CHARS = {
  1973.         '\x80': ('euro', '20AC'),
  1974.         '\x81': ' ',
  1975.         '\x82': ('sbquo', '201A'),
  1976.         '\x83': ('fnof', '192'),
  1977.         '\x84': ('bdquo', '201E'),
  1978.         '\x85': ('hellip', '2026'),
  1979.         '\x86': ('dagger', '2020'),
  1980.         '\x87': ('Dagger', '2021'),
  1981.         '\x88': ('circ', '2C6'),
  1982.         '\x89': ('permil', '2030'),
  1983.         '\x8a': ('Scaron', '160'),
  1984.         '\x8b': ('lsaquo', '2039'),
  1985.         '\x8c': ('OElig', '152'),
  1986.         '\x8d': '?',
  1987.         '\x8e': ('#x17D', '17D'),
  1988.         '\x8f': '?',
  1989.         '\x90': '?',
  1990.         '\x91': ('lsquo', '2018'),
  1991.         '\x92': ('rsquo', '2019'),
  1992.         '\x93': ('ldquo', '201C'),
  1993.         '\x94': ('rdquo', '201D'),
  1994.         '\x95': ('bull', '2022'),
  1995.         '\x96': ('ndash', '2013'),
  1996.         '\x97': ('mdash', '2014'),
  1997.         '\x98': ('tilde', '2DC'),
  1998.         '\x99': ('trade', '2122'),
  1999.         '\x9a': ('scaron', '161'),
  2000.         '\x9b': ('rsaquo', '203A'),
  2001.         '\x9c': ('oelig', '153'),
  2002.         '\x9d': '?',
  2003.         '\x9e': ('#x17E', '17E'),
  2004.         '\x9f': ('Yuml', '') }
  2005.  
  2006. if __name__ == '__main__':
  2007.     import sys
  2008.     soup = BeautifulSoup(sys.stdin)
  2009.     print soup.prettify()
  2010.  
  2011.